package com.lefeng.cockpit.datax.thread;

import com.lefeng.cockpit.common.bean.po.job.JobLogReport;
import com.lefeng.cockpit.datax.conf.JobAdminConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class JobLogReportHelper {
    private static final Logger logger = LoggerFactory.getLogger(JobLogReportHelper.class);
    private static final JobLogReportHelper instance = new JobLogReportHelper();
    private Thread logrThread;
    private volatile boolean top = false;

    private JobLogReportHelper() {
    }

    public static JobLogReportHelper getInstance() {
        return instance;
    }

    public void start() {
        logrThread = new Thread(this::run);
        logrThread.setDaemon(true);
        logrThread.setName("JobLogReportHelper");
        logrThread.start();
    }

    private void run() {
        long lastCleanLogTime = 0;

        while (!top) {
            try {
                refreshLogReports();
                cleanExpiredLogs(lastCleanLogTime);
                lastCleanLogTime = updateLastCleanLogTime();
                TimeUnit.MINUTES.sleep(1);
            } catch (Exception e) {
                if (!top) {
                    logger.error("Error in JobLogReportHelper thread: {}", e.getMessage(), e);
                    try {
                        toStop();
                        TimeUnit.MINUTES.sleep(1);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        }

        logger.info("JobLogReportHelper thread stopped");
    }

    /**
     * Refreshes log reports for the past 3 days.
     */
    private void refreshLogReports() {
        for (int i = 0; i < 3; i++) {
            LocalDateTime startOfDay = LocalDateTime.now().minusDays(i).toLocalDate().atStartOfDay();
            LocalDateTime endOfDay = startOfDay.plusDays(1).minusNanos(1);

            Date todayFrom = Date.from(startOfDay.toInstant(ZoneOffset.UTC));
            Date todayTo = Date.from(endOfDay.toInstant(ZoneOffset.UTC));

            JobLogReport report = createLogReport(todayFrom);
            Map<String, Object> triggerCountMap = JobAdminConfig.getAdminConfig().getJobLogDao().findLogReport(todayFrom, todayTo);

            if (triggerCountMap != null) {
                updateLogReport(report, triggerCountMap);
            }

            saveOrUpdateReport(report);
        }
    }

    /**
     * Cleans logs that are older than the retention period.
     *
     * @param lastCleanLogTime The timestamp of the last log cleaning operation.
     */
    private void cleanExpiredLogs(long lastCleanLogTime) {
        if (JobAdminConfig.getAdminConfig().getLogretentiondays() > 0 &&
                System.currentTimeMillis() - lastCleanLogTime > TimeUnit.DAYS.toMillis(1)) {

            Date clearBeforeTime = getRetentionDate();
            List<Long> logIds;

            do {
                logIds = JobAdminConfig.getAdminConfig().getJobLogDao().findClearLogIds(0, 0, clearBeforeTime, 0, 1000);
                if (logIds != null && !logIds.isEmpty()) {
                    JobAdminConfig.getAdminConfig().getJobLogDao().clearLog(logIds);
                }
            } while (logIds != null && !logIds.isEmpty());
        }
    }

    /**
     * Creates a new JobLogReport with the specified trigger day.
     *
     * @param triggerDay The day the report is triggered.
     * @return A new JobLogReport instance.
     */
    private JobLogReport createLogReport(Date triggerDay) {
        JobLogReport report = new JobLogReport();
        report.setTriggerDay(triggerDay);
        report.setRunningCount(0);
        report.setSucCount(0);
        report.setFailCount(0);
        return report;
    }

    /**
     * Updates the JobLogReport with data from the trigger count map.
     *
     * @param report          The JobLogReport to update.
     * @param triggerCountMap The map containing trigger count data.
     */
    private void updateLogReport(JobLogReport report, Map<String, Object> triggerCountMap) {
        Integer triggerDayCount = Integer.parseInt(triggerCountMap.get("triggerDayCount").toString());
        Integer runningCount = Integer.parseInt(triggerCountMap.get("triggerDayCountRunning").toString());
        Integer sucCount = Integer.parseInt(triggerCountMap.get("triggerDayCountSuc").toString());
        Integer failCount = triggerDayCount - runningCount - sucCount;

        report.setRunningCount(runningCount);
        report.setSucCount(sucCount);
        report.setFailCount(failCount);
    }

    /**
     * Saves or updates the JobLogReport in the database.
     *
     * @param report The JobLogReport to save or update.
     */
    private void saveOrUpdateReport(JobLogReport report) {
        int result = JobAdminConfig.getAdminConfig().getJobInfoReportDao().update(report);
        if (result < 1) {
            JobAdminConfig.getAdminConfig().getJobInfoReportDao().save(report);
        }
    }

    /**
     * Gets the date before which logs should be cleared based on the retention period.
     *
     * @return The date before which logs should be cleared.
     */
    private Date getRetentionDate() {
        LocalDateTime retentionDateTime = LocalDateTime.now()
                .minusDays(JobAdminConfig.getAdminConfig().getLogretentiondays())
                .toLocalDate().atStartOfDay();
        return Date.from(retentionDateTime.toInstant(ZoneOffset.UTC));
    }

    /**
     * Updates and returns the timestamp of the last log cleaning operation.
     *
     * @return The updated timestamp.
     */
    private long updateLastCleanLogTime() {
        return System.currentTimeMillis();
    }

    public void toStop() {
        top = true;
        logrThread.interrupt();
        try {
            logrThread.join();
        } catch (InterruptedException e) {
            logger.error("Thread interrupted: {}", e.getMessage(), e);
        }
    }
}
